﻿namespace XUCore.Serializer
{
    /********************************************************************
    *           Copyright:       2009-2011
    *           Company:         
    *           CRL Version :    4.0.30319.239
    *           Created by 徐毅 at 2011/12/9 19:53:58
    *                   mailto:3624091@qq.com
    *                         
    ********************************************************************/

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Xml.Serialization;
    using System.Xml;
    using System.Collections;
    using System.Globalization;
    using XUCore.Extensions;
    using System.Xml.Linq;

    public static class BsonExtensions
    {
        ///// <summary>
        ///// 序列化 =&gt; JSON
        ///// </summary>
        ///// <param name="obj"></param>
        ///// <param name="toUnicode"></param>
        ///// <returns></returns>
        //public static string ToBson(this BsonDocument obj, bool toUnicode = false)
        //{
        //    return BsonDocumentFormatter.Serialize(obj, toUnicode);
        //}
        ///// <summary>
        ///// 序列化 =&gt; JSONS
        ///// </summary>
        ///// <param name="objs"></param>
        ///// <param name="toUnicode"></param>
        ///// <returns></returns>
        //public static string ToBson(this List<BsonDocument> objs, bool toUnicode = false)
        //{
        //    return BsonDocumentFormatter.Serialize(objs, toUnicode);
        //}

        /// <summary>
        /// 加载json字符串
        /// </summary>
        /// <param name="json"></param>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static List<Bson> LoadBsons(this string json, bool ignoreCase = false)
        {
            List<Bson> objects = new();

            object obj = BsonConvert.Decode(json);

            if (obj is ArrayList)
            {
                ArrayList array = obj as ArrayList;

                objects = BsonDocumentDataParser.ParseRawObjects(array.GetEnumerator(), ignoreCase);
            }
            else if (obj is IDictionary)
            {
                IDictionary dictionary = obj as IDictionary;

                IEnumerator enumerable = dictionary.GetEnumerator();

                objects.Add(BsonDocumentDataParser.ParseRawObject(enumerable, ignoreCase));
            }

            //List<BsonDocument> rObjects = DataParser.RestoreBsonDocuments(objects);

            return objects;
        }
        /// <summary>
        /// 加载json字符串
        /// </summary>
        /// <param name="json"></param>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static Bson LoadBson(this string json, bool ignoreCase = false)
        {
            if (BsonConvert.Decode(json) is not IDictionary obj) return Bson.Create(ignoreCase);

            IEnumerator enumerable = obj.GetEnumerator();

            Bson _BsonDocument = BsonDocumentDataParser.ParseRawObject(enumerable, ignoreCase);

            return _BsonDocument;
        }
        /// <summary>
        /// 加载本地json数据文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static List<Bson> LoadFileBsons(this string path, bool ignoreCase = false)
        {
            return LoadFileBsons(path, Encoding.Default, ignoreCase);
        }
        /// <summary>
        /// 加载本地json数据文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="encoding"></param>
        /// <param name="ignoreCase">忽略大小写</param>
        /// <returns></returns>
        public static List<Bson> LoadFileBsons(this string path, Encoding encoding, bool ignoreCase = false)
        {
            if (System.IO.File.Exists(path))
            {
                string json = System.IO.File.ReadAllText(path, encoding);
                return LoadBsons(json, ignoreCase);
            }
            else
            {
                throw new Exception("Didn't find the document , path : " + path);
            }
        }
        /// <summary>
        /// 加载本地json数据文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        public static Bson LoadFileBson(this string path, bool ignoreCase = false)
        {
            return LoadFileBson(path, Encoding.Default, ignoreCase);
        }
        /// <summary>
        /// 加载本地json数据文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="encoding"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        public static Bson LoadFileBson(this string path, Encoding encoding, bool ignoreCase = false)
        {
            if (System.IO.File.Exists(path))
            {
                string json = System.IO.File.ReadAllText(path, encoding);
                return LoadBson(json, ignoreCase);
            }
            else
            {
                throw new Exception("Didn't find the document , path : " + path);
            }
        }

        /// <summary>
        /// 序列化 =&gt; XML
        /// </summary>
        /// <param name="objs"></param>
        /// <returns></returns>
        public static string ToXml(this List<Bson> objs)
        {
            return ToXml(typeof(List<Bson>), objs, Encoding.UTF8);
        }
        /// <summary>
        /// 序列化 =&gt; XML
        /// </summary>
        /// <param name="objs"></param>
        /// <param name="encoding">编码格式</param>
        /// <returns></returns>
        public static string ToXml(this List<Bson> objs, Encoding encoding)
        {
            return ToXml(typeof(List<Bson>), objs, encoding);
        }
        /// <summary>
        /// 序列化 =&gt; XML
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToXml(this Bson obj)
        {
            return ToXml(typeof(Bson), obj, Encoding.UTF8);
        }
        /// <summary>
        /// 序列化 =&gt; XML
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="encoding">编码格式</param>
        /// <returns></returns>
        public static string ToXml(this Bson obj, Encoding encoding)
        {
            return ToXml(typeof(Bson), obj, encoding);
        }
        /// <summary>
        /// 序列化 =&gt; XML
        /// </summary>
        /// <param name="type"></param>
        /// <param name="obj"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static string ToXml(Type type, object obj, Encoding encoding)
        {
            MemoryStream memoryStream = new MemoryStream();
            XmlTextWriter xmlTextWriter = null;
            try
            {
                XmlSerializer xs = new XmlSerializer(type);
                xmlTextWriter = new XmlTextWriter(memoryStream, encoding);
                xs.Serialize(xmlTextWriter, obj);
                memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
                return encoding.GetString((memoryStream.ToArray()));
            }
            finally
            {
                if (memoryStream != null)
                {
                    memoryStream.Dispose();
                }
                if (xmlTextWriter != null)
                {
                    xmlTextWriter.Close();
                }
            }
        }
    }


    /// <summary>
    ///   Lightweight routines to handle basic json serializing.
    /// </summary>
    internal class BsonDocumentFormatter
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="docs"></param>
        /// <param name="toUnicode"></param>
        /// <returns></returns>
        public static string Serialize(List<Bson> docs, bool toUnicode)
        {
            var json = new StringBuilder();
            json.Append("[ ");
            var first = true;
            foreach (var doc in docs)
            {
                if (first)
                    first = false;
                else
                    json.Append(", ");
                json.Append(Serialize(doc, toUnicode));
            }
            json.Append(" ]");
            return json.ToString();
        }
        /// <summary>
        ///   Serializes the specified doc.
        /// </summary>
        /// <param name = "doc">The doc.</param>
        /// <returns></returns>
        /// <param name="toUnicode"></param>
        public static string Serialize(Bson doc, bool toUnicode)
        {
            var json = new StringBuilder();
            json.Append("{ ");
            var first = true;
            foreach (var key in doc.Keys)
            {
                if (first)
                    first = false;
                else
                    json.Append(", ");
                json.AppendFormat(@"""{0}"": ", key);
                SerializeType(doc[key], json, toUnicode);
            }
            json.Append(" }");
            return json.ToString();
        }

        /// <summary>
        ///   Serializes the type.
        /// </summary>
        /// <param name = "value">The value.</param>
        /// <param name = "json">The json.</param>
        /// <param name="toUnicode"></param>
        private static void SerializeType(object value, StringBuilder json, bool toUnicode)
        {
            if (value == null)
            {
                json.Append("null");
                return;
            }
            if (value is bool)
            {
                json.Append(((bool)value) ? "true" : "false");
            }
            else if (value is int || value is long || value is float || value is double || value is decimal)
            {
                json.Append(((IFormattable)value).ToString("G", CultureInfo.InvariantCulture));
            }
            else if (value is string)
            {
                json.AppendFormat(@"""{0}""", Escape((string)value, toUnicode));
            }
            else if (value is DateTime)
            {
                json.AppendFormat(@"""{0}""", ((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss"));
            }
            else if (value is Guid)
            {
                json.Append(String.Format(@"{{ ""$uid"": ""{0}"" }}", value));
            }
            else if (value is Bson)
            {
                json.Append(Serialize(value as Bson, toUnicode));
            }
            else if (value is IEnumerable)
            {
                json.Append("[ ");
                var first = true;
                foreach (var v in (IEnumerable)value)
                {
                    if (first)
                        first = false;
                    else
                        json.Append(", ");
                    SerializeType(v, json, toUnicode);
                }
                json.Append(" ]");
            }
            else
            {
                json.AppendFormat(@"""{0}""", Escape(value.ToString(), toUnicode));
            }
            return;
        }

        /// <summary>
        ///   Escapes any characters that are special to javascript.
        /// </summary>
        public static string Escape(string text, bool toUnicode)
        {
            if (toUnicode)
                return text.ToUnicode();
            var builder = new StringBuilder();
            foreach (var c in text)
                switch (c)
                {
                    case '\b':
                        builder.Append(@"\b");
                        break;
                    case '\f':
                        builder.Append(@"\f");
                        break;
                    case '\n':
                        builder.Append(@"\n");
                        break;
                    case '\r':
                        builder.Append(@"\r");
                        break;
                    case '\t':
                        builder.Append(@"\t");
                        break;
                    case '\v':
                        builder.Append(@"\v");
                        break;
                    case '\'':
                        builder.Append(@"\'");
                        break;
                    case '"':
                        builder.Append("\\\"");
                        break;
                    case '\\':
                        builder.Append(@"\\");
                        break;
                    default:
                        if (c <= '\u001f')
                        {
                            builder.Append("\\u");
                            builder.Append(((int)c).ToString("x4"));
                        }
                        else
                            builder.Append(c);
                        break;
                }
            return builder.ToString();
        }
    }

    internal class BsonDocumentDataParser
    {
        internal static Bson ParseRawObject(IEnumerator enumerable, bool ignoreCase)
        {
            Bson oItem = Bson.Create(ignoreCase);
            while (enumerable.MoveNext())
            {
                object current = enumerable.Current;
                if (current is DictionaryEntry)
                {
                    DictionaryEntry item = (DictionaryEntry)current;
                    string mKey = (string)item.Key;
                    if (item.Value is ArrayList)
                    {
                        IEnumerator arrayListEnumerator = (item.Value as ArrayList).GetEnumerator();
                        IList<Bson> mList = ParseRawObjects(arrayListEnumerator, ignoreCase);
                        oItem[mKey] = mList;
                    }
                    else if (item.Value is IDictionary)
                    {
                        IEnumerator BsonDocumentEnumerator = (item.Value as IDictionary).GetEnumerator();

                        oItem[mKey] = ParseRawObject(BsonDocumentEnumerator, ignoreCase);
                    }
                    else
                    {
                        oItem[mKey] = item.Value;
                    }
                }
            }
            return oItem;
        }

        internal static List<Bson> ParseRawObjects(IEnumerator enumerable, bool ignoreCase)
        {
            List<Bson> list = new List<Bson>();
            while (enumerable.MoveNext())
            {
                Bson oItem = Bson.Create(ignoreCase);
                object current = enumerable.Current;
                if (current is IDictionary)
                {
                    IDictionary item = current as IDictionary;
                    foreach (string key in item.Keys)
                    {
                        if (item[key] is ArrayList)
                        {
                            IEnumerator arrayListEnumerator = (item[key] as ArrayList).GetEnumerator();
                            IList<Bson> mList = ParseRawObjects(arrayListEnumerator, ignoreCase);
                            oItem[key] = mList;
                        }
                        else if (item[key] is IDictionary)
                        {
                            IEnumerator BsonDocumentEnumerator = (item[key] as IDictionary).GetEnumerator();
                            oItem[key] = ParseRawObject(BsonDocumentEnumerator, ignoreCase);
                        }
                        else
                        {
                            oItem[key] = item[key];
                        }
                    }
                }
                list.Add(oItem);
            }
            return list;
        }
    }
}
